﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

namespace CalculatorApp { namespace Common
{
    ref class AlwaysSelectedCollectionView sealed:
        public Windows::UI::Xaml::DependencyObject,
        public Windows::UI::Xaml::Data::ICollectionView
    {
    internal:
        AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source):
            m_currentPosition(-1)
        {
            m_source = source;

            Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast<Windows::UI::Xaml::Interop::IBindableObservableVector^>(source);
            if (observable)
            {
                observable->VectorChanged +=
                    ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged);
            }
        }

    private:
        // ICollectionView
          // Not implemented methods
        virtual Windows::Foundation::IAsyncOperation<Windows::UI::Xaml::Data::LoadMoreItemsResult>^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious
        {
            throw ref new Platform::NotImplementedException();
        }
        property Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CollectionGroups
        {
            virtual Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get
            {
                return ref new Platform::Collections::Vector<Platform::Object^>();
            }
        }
        property bool HasMoreItems
        {
            virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get
            {
                return false;
            }
        }

          // Implementented methods
        virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo
        {
            if (item)
            {
                unsigned int newCurrentPosition = 0;
                bool result = m_source->IndexOf(item, &newCurrentPosition);
                if (result)
                {
                    m_currentPosition = newCurrentPosition;
                    m_currentChanged(this, nullptr);
                    return true;
                }
            }

            // The item is not in the collection
            // We're going to schedule a call back later so we
            // restore the selection to the way we wanted it to begin with
            if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
            {
                this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
                    ref new Windows::UI::Core::DispatchedHandler(
                        [this]()
                        {
                            m_currentChanged(this, nullptr);
                        }));
            }
            return false;
        }

        virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition
        {
            if (index < 0 || index >= static_cast<int>(m_source->Size))
            {
                return false;
            }

            m_currentPosition = index;
            m_currentChanged(this, nullptr);
            return true;
        }

        property Platform::Object^ CurrentItem
        {
            virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get
            {
                if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
                {
                    return m_source->GetAt(m_currentPosition);
                }
                return nullptr;
            }
        }

        property int CurrentPosition
        {
            virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get
            {
                return m_currentPosition;
            }
        }

        property bool IsCurrentAfterLast
        {
            virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get
            {
                return m_currentPosition >= static_cast<int>(m_source->Size);
            }
        }

        property bool IsCurrentBeforeFirst
        {
            virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get
            {
                return m_currentPosition < 0;
            }
        }

        event Windows::Foundation::EventHandler<Platform::Object^>^ CurrentChanged
        {
            virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::EventHandler<Platform::Object^>^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add
            {
                return m_currentChanged += handler;
            }
            virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::remove
            {
                m_currentChanged -= token;
            }
        }
        event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ CurrentChanging
        {
            virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Data::CurrentChangingEventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::add
            {
                return m_currentChanging += handler;
            }
            virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::remove
            {
                m_currentChanging -= token;
            }
        }

        // IVector<Object^>
          // Not implemented methods
        virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::Append
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void Clear() = Windows::Foundation::Collections::IVector<Platform::Object^>::Clear
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetMany
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual Windows::Foundation::Collections::IVectorView<Platform::Object^>^ GetView() = Windows::Foundation::Collections::IVector<Platform::Object^>::GetView
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::InsertAt
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAt
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAtEnd
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void ReplaceAll(const Platform::Array<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::ReplaceAll
        {
            throw ref new Platform::NotImplementedException();
        }
        virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::SetAt
        {
            throw ref new Platform::NotImplementedException();
        }

          // Implemented methods
        virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetAt
        {
            return m_source->GetAt(index);
        }

        virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector<Platform::Object^>::IndexOf
        {
            return m_source->IndexOf(item, index);
        }

        property unsigned int Size
        {
            virtual unsigned int get() = Windows::Foundation::Collections::IVector<Platform::Object^>::Size::get
            {
                return m_source->Size;
            }
        }

        // IObservableVector<Object^>
        event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ VectorChanged
        {
            virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ handler) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::add
            {
                return m_vectorChanged += handler;
            }
            virtual void remove(Windows::Foundation::EventRegistrationToken token) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::remove
            {
                m_vectorChanged -= token;
            }
        }

        // IIterable<Object^>
         // Not implemented
        virtual Windows::Foundation::Collections::IIterator<Platform::Object^>^ First() = Windows::Foundation::Collections::IIterable<Platform::Object^>::First
        {
            throw ref new Platform::NotImplementedException();
        }

        // Event handlers
        void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e)
        {
            Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast<Windows::Foundation::Collections::IVectorChangedEventArgs^>(e);
            m_vectorChanged(this, args);
        }

        Windows::UI::Xaml::Interop::IBindableVector^ m_source;
        int m_currentPosition;
        event Windows::Foundation::EventHandler<Platform::Object^>^ m_currentChanged;
        event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging;
        event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ m_vectorChanged;
    };

    public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
    {
    public:
        AlwaysSelectedCollectionViewConverter()
        { }

    private:
        virtual Platform::Object^ Convert(
            Platform::Object^ value,
            Windows::UI::Xaml::Interop::TypeName /*targetType*/,
            Platform::Object^ /*parameter*/,
            Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
        {
            auto result = dynamic_cast<Windows::UI::Xaml::Interop::IBindableVector^>(value);
            if (result)
            {
                return ref new AlwaysSelectedCollectionView(result);
            }
            return Windows::UI::Xaml::DependencyProperty::UnsetValue;  // Can't convert
        }

        virtual Platform::Object^ ConvertBack(
            Platform::Object^ /*value*/,
            Windows::UI::Xaml::Interop::TypeName /*targetType*/,
            Platform::Object^ /*parameter*/,
            Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
        {
            return Windows::UI::Xaml::DependencyProperty::UnsetValue;
        }
    };
}}
